//
// itertool - a polynormial and type-safe iterator framework in C++.
//
// Copyright (c) 2021-2025 Feng Cong
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT

#pragma once

#include <itertool/itertool_fwd.h>

#include <iterator>
#include <type_traits>

namespace itertool {

template <typename ForwardIter>
using SeqItem = decltype(*std::declval<ForwardIter>());

namespace detail {

template <typename ForwardIter, typename = void>
struct SeqRequires : std::false_type {};

template <typename ForwardIter>
struct SeqRequires<
    ForwardIter,
    std::void_t<decltype(*std::declval<ForwardIter>()),
                decltype(std::declval<ForwardIter>() !=
                         std::declval<ForwardIter>()),
                decltype(++std::declval<ForwardIter>()),
                std::enable_if_t<std::is_copy_assignable_v<ForwardIter>>,
                std::enable_if_t<std::is_copy_constructible_v<ForwardIter>>>>
    : std::true_type {};

template <typename ForwardIter>
class SeqIterable : public Iterable<SeqItem<ForwardIter>> {
  static_assert(SeqRequires<ForwardIter>::value,
                "Seq has the following requirements on type ForwardIter: *it, "
                "a != b, ++it, "
                "copy_assignable, and copy_constructible");

 public:
  using ItemType = SeqItem<ForwardIter>;

  SeqIterable(ForwardIter b, ForwardIter e) : begin_(b), end_(e), now_(b) {}

  bool HasNext() override { return now_ != end_; }
  ItemType GetNow() override { return std::forward<ItemType>(*now_); }
  void Advance() override { ++now_; }

  bool IsFinite() override { return true; }
  bool IsResetable() override { return true; }
  void Reset() override { now_ = begin_; }
  size_t LengthHint() override { return std::distance(now_, end_); }

 private:
  ForwardIter begin_, end_, now_;
};

}  // namespace detail

template <typename ForwardIter>
Iterator<SeqItem<ForwardIter>> Seq(ForwardIter first, ForwardIter last) {
  return MakeIterator<detail::SeqIterable<ForwardIter>>(first, last);
}

template <typename Container>
auto Seq(Container& c) -> Iterator<SeqItem<decltype(std::begin(c))>> {
  return Seq(std::begin(c), std::end(c));
}

}  // namespace itertool
